import {
  BadRequestException,
  Injectable,
  InternalServerErrorException,
} from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { HttpService } from "@nestjs/axios";
import { lastValueFrom } from "rxjs";
import { InjectRepository } from "@nestjs/typeorm";
import { User } from "/@/entities/user/User.entity";
import { Repository } from "typeorm";
import { JwtService } from "@nestjs/jwt";
import { RedisCacheService } from "/@/common/redis/redisCache.service";
import {
  AddAddressDto,
  DelAddressDto,
  EditAddressDto,
} from "./dto/address.dto";
import { Address } from "/@/entities/user/Address.entity";
import { RangeEnum } from "/@/enum/range.enum";
@Injectable()
export class UserService {
  appid: string;
  appSecret: string;
  grant_type = "authorizationCode";
  constructor(
    @InjectRepository(User)
    private userRepository: Repository<User>,
    @InjectRepository(Address)
    private addressRepository: Repository<Address>,
    private readonly configService: ConfigService,
    private readonly jwtService: JwtService,
    private readonly httpService: HttpService,
    private readonly redisCacheService: RedisCacheService,
  ) {
    this.appid = configService.get<string>("weapp.appid");
    this.appSecret = configService.get<string>("weapp.appSecret");
  }

  // 校验用户信息
  async validateUser(openid: string, session_key: string): Promise<any> {
    const user = await this.findOneByOpenId(openid);
    // 已经存在该 openID的用户 重新生成一个token替换就好了
    if (user) {
      // 已经有这个用户，直接颁发token
      return this.certificate(user);
    }
    // 没有则进行注册，再给token
    //新增用户
    const entity = this.userRepository.create({
      openid,
      session_key,
    });
    try {
      const data = await this.userRepository.save(entity);
      // 新增用户之后再调用 certificate 颁发token
      return this.certificate(data);
    } catch (error) {
      throw new InternalServerErrorException("用户创建失败");
    }
  }

  // 准备颁发token
  async certificate(user: User) {
    // 即将生成token的payload
    const payload = { id: user.id };
    try {
      // 生成token
      const token = this.jwtService.sign(payload);
      const key = `weapp:${user.id}`;
      // 更新写入Redis
      this.redisCacheService.set(key, token, 168 * 60 * 55);
      return { token };
    } catch (error) {
      throw new InternalServerErrorException("签证失败");
    }
  }

  /**
   * 查询该openid用户
   * @param params 字段
   */
  async findOneByOpenId(openid: string): Promise<any | undefined> {
    let qb = this.userRepository.createQueryBuilder("user");
    qb = qb.where("user.openid = :openid", { openid });
    const data = await qb.getOne();
    return data;
  }

  // 验证token并返回user信息
  async getUserByToken(token: any) {
    const user = await this.jwtService.verify(token);
    return user;
  }

  // 获取Redis key
  async getRedisValue(user: any) {
    const key = `weapp:${user.id}`;
    const redisValue = await this.redisCacheService.get(key);
    return redisValue;
  }

  // 小程序登录
  async login(code: string) {
    try {
      // 用code跟微信请求登录，获取openID
      const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${this.appid}&secret=${this.appSecret}&js_code=${code}&grant_type=${this.grant_type}`;
      // http请求模块 定义了一个get请求
      const requestObs = this.httpService.get(url);
      // 获取到微信服务器给的openID，session_key
      const { data } = await lastValueFrom(requestObs);
      console.log(data);
      // 验证openid
      const authResult = await this.validateUser(data.openid, data.session_key);

      return authResult;
    } catch (error) {
      throw new InternalServerErrorException("微信登录失败，请重新打开小程序");
    }
  }

  /**
   * 获取用户信息
   * @param query 字段
   */
  async getUserInfo(user: any) {
    try {
      const { id } = user;
      const data = await this.userRepository.findOne({ id });
      console.log(id, data);
      delete data.openid;
      return data;
    } catch (error) {
      throw new InternalServerErrorException(error.response || "获取失败");
    }
  }

  /**
   * 绑定用户信息
   * @param params 字段
   */
  async bindUserInfo(params: any, user: any) {
    try {
      delete params.language;
      const { id } = user;
      await this.userRepository.update(id, params);
      return await this.userRepository.findOne(id);
    } catch (error) {
      console.log(error);
      throw new InternalServerErrorException(error.response || "添加失败");
    }
  }

  // 收货地址相关
  /**
   * 获取收货地址
   * @param query 字段
   */
  async getAddress(query, user: any) {
    try {
      const { id } = query;
      const { id: user_id } = user;
      let qb = this.addressRepository.createQueryBuilder("address");

      qb = qb
        .where("user_id = :user_id", { user_id })
        .orderBy("address.default", "ASC")
        .addOrderBy("address.update_at", "DESC");
      if (id) {
        qb = qb.andWhere("address.id = :id", { id });
      }
      const data = await qb.getMany();
      return data;
    } catch (error) {
      throw new InternalServerErrorException(error.response || "获取失败");
    }
  }
  /**
   * 添加收货地址  最多八个
   * @param params 字段
   */
  async addAddress(params: AddAddressDto, user: any) {
    try {
      const { id: user_id } = user;
      // 获取用户当前已有的地址列表
      const [items, total] = await this.addressRepository
        .createQueryBuilder("address")
        .where("user_id = :user_id", { user_id })
        .orderBy("address.update_at", "ASC")
        .getManyAndCount();
      // 默认限制只能大于8个
      if (total >= RangeEnum.MAX_ADDRESS) {
        throw new BadRequestException(
          `一个用户最多设置${RangeEnum.MAX_ADDRESS}个地址`,
        );
      }
      // 创建地址实体
      const entity = this.addressRepository.create({ ...params, user_id });
      await this.addressRepository.save(entity);

      // 如果当前设置了默认地址 并且之前有数据
      if (params.default === "1" && total > 0) {
        items.forEach((item) => {
          const id = item.id;
          item.default = "2";
          this.addressRepository.update(id, item);
        });
      }
      return "添加成功";
    } catch (error) {
      throw new InternalServerErrorException(
        error.response || "添加失败，请检查",
      );
    }
  }
  /**
   * 编辑收货地址
   * @param params 字段
   */
  async editAddress(params: EditAddressDto, user: any) {
    const { id: user_id } = user;
    const { id } = params;
    try {
      await this.addressRepository.update(id, params);
      const [items, total] = await this.addressRepository
        .createQueryBuilder("address")
        .where("user_id = :user_id", { user_id })
        .andWhere("id != :id", { id })
        .orderBy("address.update_at", "ASC")
        .getManyAndCount();

      if (params.default === "1" && total > 0) {
        for (let i = 0; i < items.length; i++) {
          const id = items[i].id;
          items[i].default = "2";
          await this.addressRepository.update(id, items[i]);
        }
      }
      return "编辑成功";
    } catch (error) {
      throw new InternalServerErrorException("编辑失败");
    }
  }

  /**
   * 删除收货地址
   * @param params 字段
   */
  async delAddress(params: DelAddressDto) {
    const { id } = params;
    try {
      await this.addressRepository.delete({ id });
      return "删除成功";
    } catch (error) {
      throw new InternalServerErrorException("删除失败");
    }
  }
}
